package com.baidu.sggc.service;

import com.baidu.sggc.core.common.utils.MenuPathUtil;
import com.baidu.sggc.entity.AuditQueryLog;
import com.baidu.sggc.entity.OperateType;
import com.baidu.sggc.mapper.AuditQueryLogMapper;
import com.baidu.sggc.mapper.SyncFailureLogMapper;
import com.baidu.sggc.mapper.SyncOffsetMapper;
import com.baidu.sggc.entity.SyncOffset;
import com.baidu.sggc.entity.SyncFailureLog;
import com.baidu.sggc.core.common.utils.AuditDescUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Slf4j
@Service
@RequiredArgsConstructor
public class AuditQueryLogSyncService {

    private static final String OFFSET_KEY = "audit_query_log_to_event";

    private final SyncOffsetMapper syncOffsetMapper;
    private final SyncFailureLogMapper syncFailureLogMapper;
    private final AuditQueryLogMapper auditQueryLogMapper;
    private final RestTemplate restTemplate;

    @Value("${audit.sync.remote-url}")
    private String remoteUrl;

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @Transactional
    public void syncAuditQueryLogs() {
        SyncOffset offset = syncOffsetMapper.selectById(OFFSET_KEY);
        long lastOffset = offset != null ? offset.getOffset() : 0L;

        List<AuditQueryLog> logs = auditQueryLogMapper.selectNewLogs(lastOffset, 100);
        log.info("【查询日志同步】当前offset={}，拉取日志数量={}", lastOffset, logs.size());

        if (logs.isEmpty()) {
            return;
        }

        long start = System.currentTimeMillis();
        int success = 0;
        int fail = 0;

        for (AuditQueryLog logEntry : logs) {
            Long pk = logEntry.getId();

            logEntry.setMenuPath(MenuPathUtil.formatMenuPath(logEntry.getMenuPath()));
            // menuPath 判空过滤
            if (logEntry.getMenuPath() == null || logEntry.getMenuPath().trim().isEmpty()) {
                log.info("【过滤日志】日志ID={}，menuPath为空，跳过发送并推进 offset", pk);
                updateOffset(pk);
                continue;
            }

            try {
                Map<String, Object> body = buildRequestBody(logEntry);
                log.info("【构建请求体】日志ID={}，请求体={}", pk, body);

                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);

                ResponseEntity<String> response = restTemplate.postForEntity(remoteUrl, request, String.class);
                log.info("【接口响应】日志ID={}，状态={}，响应体={}", pk, response.getStatusCode(), response.getBody());

                if (response.getStatusCode().is2xxSuccessful()) {
                    updateOffset(pk);
                    success++;
                } else {
                    log.warn("【同步失败】日志ID={}，状态={}，响应体={}", pk, response.getStatusCodeValue(), response.getBody());
                    recordFailure(pk, "接口状态码：" + response.getStatusCodeValue());
                    fail++;
                }
            } catch (Exception e) {
                log.error("【同步异常】日志ID={}，异常：{}", pk, e.getMessage(), e);
                recordFailure(pk, "异常：" + e.getMessage());
                fail++;
            }
        }

        long cost = System.currentTimeMillis() - start;
        log.info("【查询日志同步完成】处理数量={}，成功={}，失败={}，耗时={}ms", logs.size(), success, fail, cost);
    }

    private Map<String, Object> buildRequestBody(AuditQueryLog log) {
        String operation = log.getOperation();
        Map<String, Object> map = new HashMap<>();
        map.put("userId", log.getUserId());

        String rawUserName = log.getUserName();
        String cleanedUserName = (rawUserName != null && rawUserName.startsWith("user_"))
                ? rawUserName.substring(5)
                : rawUserName;
        map.put("userName", cleanedUserName);
        map.put("eventId", "BIZ");
        map.put("operateId", operation == null ? null :
                Optional.ofNullable(OperateType.getByEnName(operation))
                        .map(OperateType::getZhName)
                        .orElse(operation));
        map.put("domain", log.getMenuPath());
        map.put("userIp", log.getAccessAddress());
        map.put("sessionId", extractUniqueId(log.getExtendData()));
        map.put("appId", "大模型工具链平台");
        map.put("desc", AuditDescUtil.buildQueryDesc(log));  // 你需扩展 buildDesc 支持 AuditQueryLog
        map.put("isSuccess", "success".equalsIgnoreCase(log.getOperateState()));
        map.put("exceptionLevel", "success".equalsIgnoreCase(log.getOperateState()) ? "0" : "1");
        return map;
    }

    private String extractUniqueId(String extendDataJson) {
        try {
            JsonNode node = OBJECT_MAPPER.readTree(extendDataJson);
            return node.has("unique_identification") ? node.get("unique_identification").asText() : "";
        } catch (Exception e) {
            return "";
        }
    }

    private void updateOffset(Long pk) {
        SyncOffset offset = new SyncOffset();
        offset.setId(OFFSET_KEY);
        offset.setOffset(pk);
        offset.setUpdateTime(LocalDateTime.now());
        syncOffsetMapper.insertOrUpdate(offset);
    }

    private void recordFailure(Long pk, String msg) {
        SyncFailureLog failLog = new SyncFailureLog();
        failLog.setAuditLogPk(pk);
        failLog.setErrorMessage(msg);
        failLog.setFailTime(new Date());
        failLog.setRetryCount(0);
        syncFailureLogMapper.insert(failLog);
    }
}